package com.custom.action.core.override;

import com.custom.comm.utils.CustomUtil;
import com.custom.comm.utils.ReflectUtil;
import com.custom.jdbc.configuration.DbGlobalConfig;
import com.custom.jdbc.executebody.SqlExecutorBody;
import com.custom.jdbc.executor.convert.AbstractRsHandler;
import com.custom.jdbc.handler.TypeHandler;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author XiaoBai
 * @since 2024/3/30 0030 21:14
 * 用户可自定义的类型处理
 */
@SuppressWarnings("unchecked")
public class CustomTypeMappedHandler extends AbstractRsHandler {

    private static final Map<Class<?>, Map<String, CustomMappedCache>> CUSTOM_MAPPED_CACHE = new ConcurrentHashMap<>();

    public CustomTypeMappedHandler(DbGlobalConfig globalConfig) {
        super(globalConfig);
    }


    private <T> Map<String, CustomMappedCache> getMappedCache(Class<T> mappedType) throws IntrospectionException {
        Map<String, CustomMappedCache> mappedCacheMap = CUSTOM_MAPPED_CACHE.get(mappedType);
        if (mappedCacheMap == null) {
            List<PropertyDescriptor> descriptors = ReflectUtil.getProperties(mappedType);
            mappedCacheMap = descriptors.stream().map(CustomMappedCache::new).collect(Collectors.toMap(e -> e.fieldName, e -> e));
            CUSTOM_MAPPED_CACHE.put(mappedType, mappedCacheMap);
        }
        return mappedCacheMap;
    }


    @Override
    public <T> T getTargetObject(SqlExecutorBody executorBody, Class<T> mappedType, ResultSet rs) throws Exception {
        Map<String, Object> resultMap = resToMap(rs);
        T instance = ReflectUtil.getInstance(mappedType);
        Map<String, CustomMappedCache> mappedCacheMap = getMappedCache(mappedType);
        Map<String, TypeHandler<?>> fieldTypeHandlerMap = executorBody.getFieldTypeHandlerMap();

        for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
            String label = entry.getKey();
            Object value = entry.getValue();
            if (isUnderlineToCamel()) {
                label = CustomUtil.underlineToCamel(label);
            }
            if (value != null) {
                CustomMappedCache mappedCache = mappedCacheMap.get(label);
                if (mappedCache != null) {
                    try {
                        if (fieldTypeHandlerMap != null) {
                            TypeHandler<?> typeHandler = fieldTypeHandlerMap.get(label);
                            value = typeHandler.getTypeValue(value);
                        }
                        mappedCache.writer.invoke(instance, value);
                    } catch (IllegalArgumentException | ClassCastException e) {
                        log.warn("Type mapped handler setting error, resulting in mismatch with actual value, " +
                                        "target property: {}({}), real value: {}({})",
                                label, mappedCache.getReader().getReturnType().getSimpleName(),
                                value, value.getClass().getSimpleName());
                        throw e;
                    }
                }
            }
        }

        return instance;
    }

    @Override
    public void setUpdateParam(SqlExecutorBody executorBody, PreparedStatement statement) throws Exception {
        List<String> updateFields = executorBody.getUpdateFields();
        Object[] parameters = executorBody.getSqlParams();
        if (parameters == null || parameters.length == 0) {
            return;
        }
        for (int i = 0; i < parameters.length; i++) {
            setObjTypeHandle(statement, parameters, i, updateFields, executorBody.getFieldTypeHandlerMap());
        }
    }

    private static <V> void setObjTypeHandle(PreparedStatement statement, Object[] parameters, int i,
                                             List<String> updateFields, Map<String, TypeHandler<?>> fieldTypeHandlerMap) throws Exception {
        V parameter = (V) parameters[i];
        // 在修改时，如果updateFields只保存了需要修改的java字段，而并没有条件字段
        if (CustomUtil.listSize(updateFields) > i) {
            String fieldName = updateFields.get(i);
//            log.info("fieldName ==> {}, parameter ==> {}", fieldName, parameter);
            TypeHandler<V> typeHandler = (TypeHandler<V>) fieldTypeHandlerMap.get(fieldName);
            typeHandler.setTypeValue(statement, (i + 1), parameter);
        }else {
            statement.setObject((i + 1), parameter);
        }
    }

    @Slf4j
    @Getter
    public static class CustomMappedCache {

        private final String fieldName;
        private final Method writer;
        private final Method reader;

        public CustomMappedCache(PropertyDescriptor descriptor) {
            this.fieldName = descriptor.getName();
            this.writer = descriptor.getWriteMethod();
            this.reader = descriptor.getReadMethod();
        }
    }

}
